﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using DslModeling=global::Microsoft.VisualStudio.Modeling;
using DslDesign=global::Microsoft.VisualStudio.Modeling.Design;
using DslValidation=global::Microsoft.VisualStudio.Modeling.Validation;

namespace Salamanca.Saml
{

    [DslValidation.ValidationState(DslValidation.ValidationState.Enabled)]
    partial class ActivityModel
    {
        /// <summary>Makes sure that every state is reachable by the activity flow and that every state can lead to the end of the activity.</summary>
        [DslValidation.ValidationMethod(DslValidation.ValidationCategories.Menu | DslValidation.ValidationCategories.Open | DslValidation.ValidationCategories.Save)]
        private void ValidateFlow(DslValidation.ValidationContext context)
        {
            // Every activity state must be reachable from a start point
            SortedList<Guid, FlowElement> reachableFromStart=new SortedList<Guid,FlowElement>();
            foreach (StartPoint start in Partition.ElementDirectory.FindElements<StartPoint>(true))
                AddReachableFromStart(start, reachableFromStart);

            DslModeling.ModelElement[] nrfs=Partition.ElementDirectory.FindElements<FlowElement>(true)
                .Where<FlowElement>(fe => !reachableFromStart.ContainsKey(fe.Id))
                .Cast<DslModeling.ModelElement>()
                .ToArray<DslModeling.ModelElement>();

            if (nrfs.Length>0)
                context.LogError(
                    Resources.SR.StatesUnreachableFromStartPoint,
                    "SAML03",
                    nrfs
                );

            // Every activity state must lead to an end point
            SortedList<Guid, FlowElement> reachableFromEnd=new SortedList<Guid, FlowElement>();
            foreach (EndPoint end in Partition.ElementDirectory.FindElements<EndPoint>(true))
                AddReachableFromEnd(end, reachableFromEnd);

            DslModeling.ModelElement[] nrfe=Partition.ElementDirectory.FindElements<FlowElement>(true)
                .Where<FlowElement>(fe => !reachableFromEnd.ContainsKey(fe.Id))
                .Cast<DslModeling.ModelElement>()
                .ToArray<DslModeling.ModelElement>();

            if (nrfe.Length>0)
                context.LogError(
                    Resources.SR.FlowDoesNotLeadToEndPoint,
                    "SAML03",
                    nrfe
                );
        }

        /// <summary>Adds the specified element to the specified dictionary and tries to add subsequent elements down the activity flow.</summary>
        private static void AddReachableFromStart(FlowElement element, IDictionary<Guid, FlowElement> rfs)
        {
            if (rfs.ContainsKey(element.Id))
                return;

            rfs.Add(element.Id, element);
            foreach (FlowElement fe in element.TransitionTo)
                AddReachableFromStart(fe, rfs);
        }

        /// <summary>Adds the specified element to the specified dictionary and tries to add previous elements up the activity flow.</summary>
        private static void AddReachableFromEnd(FlowElement element, IDictionary<Guid, FlowElement> rfe)
        {
            if (rfe.ContainsKey(element.Id))
                return;

            rfe.Add(element.Id, element);
            foreach (FlowElement fe in element.TransitionFrom)
                AddReachableFromEnd(fe, rfe);
        }
    }
}
